/****************************************************************************************
**  File: Vector3.hpp
**  Author: Asteroth
**  Date: 22/02/2009
****************************************************************************************/

#ifndef __VECTOR3_HPP__
#define __VECTOR3_HPP__

template <class T>
class Vector3x {
public:
	T X, Y, Z;

	Vector3x():								X(0), Y(0), Z(0) {}
	Vector3x(const Vector2x<T> &vec):		X(vec.X), Y(vec.Y), Z(0) {}
	Vector3x(const Vector2x<T> &vec, T z):	X(vec.X), Y(vec.Y), Z(z) {}
	Vector3x(const Vector3x<T> &vec):		X(vec.X), Y(vec.Y), Z(vec.Z) {}
	Vector3x(T x, T y, T z):				X(x), Y(y), Z(z) {}

	inline Vector3x<T> &operator = (const Vector3x<T> &set) {
		X = set.X;
		Y = set.Y;
		Z = set.Z;
		return *this;
	}

	inline Vector3x<T> &operator ^= (const Matrix4x4x<T> &mat) {
		Vector3x<T> vec(*this);
		X = vec.X * mat.M[0] + vec.Y * mat.M[4] + vec.Z * mat.M[8] + mat.M[12];
		Y = vec.X * mat.M[1] + vec.Y * mat.M[5] + vec.Z * mat.M[9] + mat.M[13];
		Z = vec.X * mat.M[2] + vec.Y * mat.M[6] + vec.Z * mat.M[10] + mat.M[14];
		return *this;
	}

	inline Vector3x<T> operator ^ (const Matrix4x4x<T> &mat) const {
		return Vector3x<T>(X * mat.M[0] + Y * mat.M[4] + Z * mat.M[8] + mat.M[12],
			X * mat.M[1] + Y * mat.M[5] + Z * mat.M[9] + mat.M[13],
			X * mat.M[2] + Y * mat.M[6] + Z * mat.M[10] + mat.M[14]);
	}

	inline Vector3x<T> &operator += (const Vector3x<T> &add) {
		X += add.X;
		Y += add.Y;
		Z += add.Z;
		return *this;
	}

	inline Vector3x<T> &operator -= (const Vector3x<T> &sub) {
		X -= sub.X;
		Y -= sub.Y;
		Z -= sub.Z;
		return *this;
	}

	inline Vector3x<T> operator + (const Vector3x<T> &add) const {
		return Vector3x<T>(X + add.X, Y + add.Y, Z + add.Z);
	}

	inline Vector3x<T> operator - (const Vector3x<T> &sub) const {
		return Vector3x<T>(X - sub.X, Y - sub.Y, Z - sub.Z);
	}

	inline Vector3x<T> operator - () const {
		return Vector3x<T>(-X, -Y, -Z);
	}

	inline Vector3x<T> &operator *= (const T scalar) {
		X *= scalar;
		Y *= scalar;
		Z *= scalar;
		return *this;
	}

	inline Vector3x<T> &operator /= (const T scalar) {
		X /= scalar;
		Y /= scalar;
		Z /= scalar;
		return *this;
	}

	inline Vector3x<T> operator * (const T scalar) const {
		return Vector3x<T>(X * scalar, Y * scalar, Z * scalar);
	}

	inline Vector3x<T> operator / (const T scalar) const {
		return Vector3x<T>(X / scalar, Y / scalar, Z / scalar);
	}

	// Dot Product
	inline T operator || (const Vector3x<T> &dot) const {
		return X * dot.X + Y * dot.Y + Z * dot.Z;
	}

	// Cross Product
	inline Vector3x<T> operator && (const Vector3x<T> &crs) const {
		return Vector3x<T>(Y * crs.Z - Z * crs.Y, Z * crs.X - X * crs.Z, X * crs.Y - Y * crs.X);
	}

	// Normal
	inline Vector3x<T> operator ! () const {
		float len = sqrt(X * X + Y * Y + Z * Z);
		if (len == 0)
			len = 1;
		return Vector3x<T>(X / len, Y / len, Z / len);
	}

	// Length
	inline T operator ~ () const {
		return sqrt(X * X + Y * Y + Z * Z);
	}

	inline T dot(const Vector3x<T> &dot) const {
		return X * dot.X + Y * dot.Y + Z * dot.Z;
	}

	inline Vector3x<T> cross(const Vector3x<T> &crs) const {
		return Vector3x<T>(Y * crs.Z - Z * crs.Y, Z * crs.X - X * crs.Z, X * crs.Y - Y * crs.X);
	}

	inline Vector3x<T> normal() const {
		float len = sqrt(X * X + Y * Y + Z * Z);
		if (len == 0)
			len = 1;
		return Vector3x<T>(X / len, Y / len, Z / len);
	}

	inline T length() const {
		return sqrt(X * X + Y * Y + Z * Z);
	}

	inline T inverse() const {
		return Vector3x<T>(-X, -Y, -Z);
	}

	inline Vector3x<T> rotatedX(T angle) const {
		T hypotenuse = sqrt(Y*Y + Z*Z);
		return Vector3x<T>(X, hypotenuse * cos(angle), hypotenuse * sin(angle));
	}

	inline Vector3x<T> rotatedY(T angle) const {
		T hypotenuse = sqrt(X*X + Z*Z);
		return Vector3x<T>(hypotenuse * sin(angle), Y, hypotenuse * cos(angle));
	}

	inline Vector3x<T> rotatedZ(T angle) const {
		T hypotenuse = sqrt(Y*Y + X*X);
		return Vector3x<T>(hypotenuse * cos(angle), hypotenuse * sin(angle), Z);
	}

	inline Vector3x<T> transformed(const Matrix4x4x<T> &mat) const {
		return Vector3x<T>(X * mat.M[0] + Y * mat.M[4] + Z * mat.M[8] + mat.M[12],
			X * mat.M[1] + Y * mat.M[5] + Z * mat.M[9] + mat.M[13],
			X * mat.M[2] + Y * mat.M[6] + Z * mat.M[10] + mat.M[14]);
	}

	inline Vector3x<T> rotated(const Matrix4x4x<T> &mat) const {
		return Vector3x<T>(X * mat.M[0] + Y * mat.M[4] + Z * mat.M[8],
			X * mat.M[1] + Y * mat.M[5] + Z * mat.M[9],
			X * mat.M[2] + Y * mat.M[6] + Z * mat.M[10]);
	}

	inline void normalize() {
		T len = sqrt(X*X + Y*Y + Z*Z);
		if (len>0)
		{
			X /= len;
			Y /= len;
			Z /= len;
		}
	}

	inline void invert() {
		X = -X;
		Y = -Y;
		Z = -Z;
	}

	inline void rotateX(T angle) {
		T hypotenuse = sqrt(Y*Y + Z*Z);
		Y = hypotenuse * cos(angle);
		Z = hypotenuse * sin(angle);
	}

	inline void rotateY(T angle) {
		T hypotenuse = sqrt(Z*Z + X*X);
		Z = hypotenuse * cos(angle);
		X = hypotenuse * sin(angle);
	}

	inline void rotateZ(T angle) {
		T hypotenuse = sqrt(X*X + Y*Y);
		X = hypotenuse * cos(angle);
		Y = hypotenuse * sin(angle);
	}

	inline void transform(const Matrix4x4x<T> &mat) {
		Vector3x<T> vec(*this);
		X = vec.X * mat.M[0] + vec.Y * mat.M[4] + vec.Z * mat.M[8] + mat.M[12];
		Y = vec.X * mat.M[1] + vec.Y * mat.M[5] + vec.Z * mat.M[9] + mat.M[13];
		Z = vec.X * mat.M[2] + vec.Y * mat.M[6] + vec.Z * mat.M[10] + mat.M[14];
	}

	inline void rotate(const Matrix4x4x<T> &mat) {
		Vector3x<T> vec(*this);
		X = vec.X * mat.M[0] + vec.Y * mat.M[4] + vec.Z * mat.M[8];
		Y = vec.X * mat.M[1] + vec.Y * mat.M[5] + vec.Z * mat.M[9];
		Z = vec.X * mat.M[2] + vec.Y * mat.M[6] + vec.Z * mat.M[10];
	}
};

typedef Vector3x<float> Vector3;

#endif
